/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package myAI;

import java.util.Vector;

/**
 * Implémentation de l'algorithme NegAlphaBeta
 *
 * @author root
 *
 */
public class AlgoNegAlphaBeta implements IAlgo {

        /**
         * Nombre de feuilles et de noeuds explorés pour les statistiques
         * ainsi que le nombre de coupes
         */
        protected int mNbFeuilles = 0;

        protected int mNbNoeuds = 0;

        protected int mNbCoupes = 0;

        /**
         * Sauvegarde du meilleur coup
         */
        protected Coup mMeilleurCoup = null;

        /**
         * Horizon fixé de NegAlphaBeta
         */
        protected int mHorizon = 6;

        /**
         * Profondeur courante du NegAlphaBeta
         */
        protected int mProfondeurCourante = 0;

        /**
         * Listes des coups permis à chaque profondeur
         */
        protected Vector<Vector<Coup>> mCoupsPermis = null;

        /**
         * Heuristique utilisée
         */
        protected IHeuristique mHeuristique = null;

        /**
         * Retourne une chaine avec les statistiques courantes
         *
         * @return
         */
        public String Statistiques() {
                return "\nHorizon\t\t: " + this.mHorizon + "\nNbNoeuds\t: "
                                + this.mNbNoeuds + "\nNbFeuilles\t: " + this.mNbFeuilles + "\nNbCoupes\t: " + this.mNbCoupes;
        }

        /**
         * Cherche le meilleur coup à jouer pour une couleur donnée et un plateau
         * donné
         */
        public Coup meilleurCoup(Plateau plateau, int couleur) {
                // Réinitialisation du meilleur coup
                this.mMeilleurCoup = null;

                // information de débug
                String avantJouer = plateau.toString();

                // Lancement de l'algo AlphaBeta
                this.mProfondeurCourante = 0;
                this.NegAlphaBeta(plateau, IHeuristique.MIN_VALUE, IHeuristique.MAX_VALUE,
                                couleur);

                // information de débug
                String apresJouer = plateau.toString();
                if (!avantJouer.equals(apresJouer)) {
                        System.err
                                        .println("Erreur NegAlphaBeta : Plateau modifié illégalement");
                }

                if (this.mMeilleurCoup == null) {
                        System.err.println("Meilleur coup null !");

                        // On joue le premier coup venu... on ne sait jamais...
                        if(this.mCoupsPermis.size() > 0 && this.mCoupsPermis.elementAt(0).size() > 0)
                        {
                                return this.mCoupsPermis.elementAt(0).elementAt(0);
                        }
                }

                // Affichage des statistiques de la recherche du meilleur coup
                if(Plateau.PRINTLOG)
                {
                        System.out.println(this.Statistiques());
                }

                return this.mMeilleurCoup;
        }

        /**
         * Algorithme NegAlphaBeta
         *
         * @param plateau
         * @param alpha
         * @param beta
         * @param joueur
         * @return
         */
        public int NegAlphaBeta(Plateau plateau, int alpha, int beta, int joueur) {
                this.mProfondeurCourante++;

                if (this.EstFeuille(plateau)) {
                        this.mProfondeurCourante--;
                        return this.evaluePlateau(plateau, joueur);
                }

                int cout;
                Plateau nouveauPlateau;

                // pour éviter de réallouer à chaque fois un vector de coups, on
                // réutilise en faisant un clear() avant
                Vector<Coup> coupsPermis = this.mCoupsPermis
                                .elementAt(this.mProfondeurCourante - 1);
                coupsPermis.clear();
                // calcul de tous les coups autorisés
                plateau.coupsPermis(joueur, coupsPermis);

                /*if(Plateau.PRINTLOG)
                {
                        System.out.println("Facteur de branchement : " + coupsPermis.size());
                }*/

                for (Coup coup : coupsPermis) {
                        // information de débug
                        // System.out.println("Plateau avant de jouer \n" +
                        // plateau.toString());
                        // String avantJouer = plateau.toString();

                        // On jouer un coup
                        nouveauPlateau = plateau.jouerCoup(coup, true);

                        cout = -this.NegAlphaBeta(nouveauPlateau, -beta, -alpha, HolyKnight
                                        .joueurSuivant(joueur));

                        // On le défait : ainsi pas d'instantiation de nouveau plateau
                        // inutilement
                        plateau.defaireCoup(coup);

                        // information de débug
                        /*
                         * String apresJouer = plateau.toString();
                         * if(!avantJouer.equals(apresJouer)) { System.err.print("Erreur
                         * NegAlphaBeta interne "); if(plateau == nouveauPlateau) {
                         * System.err.println("sans plateau remplacé !"); } else {
                         * System.err.println("avec plateau remplacé !"); }
                         * System.err.println("Plateau initial :");
                         * System.err.println(avantJouer); System.err.println("Coup joué : " +
                         * coup.toString()); System.err.println("Plateau modifié >_<");
                         * System.err.println(apresJouer); }
                         */
                        // System.out.println("Plateau rétabli \n" + plateau.toString());

                        if (cout > alpha) {
                                alpha = cout;

                                // Sauvegarde du meilleur coup
                                if (this.mProfondeurCourante == 1) {
                                        this.mMeilleurCoup = coup;

                                        if(Plateau.PRINTLOG)
                                        {
                                                System.out.println("Meilleur heuristique à " + cout
                                                                + " pour le coup " + coup.toString());
                                        }
                                }

                                if (alpha >= beta) {

                                        if(Plateau.PRINTLOG)
                                        {
                                                this.mNbCoupes++;
                                        }

                                        this.mProfondeurCourante--;
                                        return alpha;
                                }
                        }
                }

                this.mProfondeurCourante--;
                return alpha;
        }

        // l'un des deux joueur a gagné ou la profondeur est atteinte
        public boolean EstFeuille(Plateau plateau) {
                if (this.mProfondeurCourante == this.mHorizon) {

                        if(Plateau.PRINTLOG)
                        {
                                this.mNbFeuilles++;
                        }

                        return true;
                }


                if (plateau.estFinal()) {

                        if(Plateau.PRINTLOG)
                        {
                                this.mNbFeuilles++;
                        }

                        return true;
                }

                this.mNbNoeuds++;
                return false;
        }

        /**
         * évalue un plateau pr une couleur donnée avec l'heuristique définie
         *
         * @param plateau
         * @param joueur
         * @return
         */
        public int evaluePlateau(Plateau plateau, int joueur) {
                return this.mHeuristique.evaluePlateau(plateau, joueur);
        }

        /**
         * Contructeur
         *
         * @param heuristique
         */
        public AlgoNegAlphaBeta(IHeuristique heuristique) {
                this.mHeuristique = heuristique;

                this.mCoupsPermis = new Vector<Vector<Coup>>(this.mHorizon);
                for (int i = 0; i < this.mHorizon; i++) {
                        this.mCoupsPermis.add(new Vector<Coup>());
                }
        }

}

